/*
 * This file contains functions which send out an v2 SNMP notification(trap)
 * to a target machine whose address is configurable in kamailio.cfg.
 */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

#include "QuantumSipServerNotifications.h"

#include "snmpstats_globals.h"

extern const oid snmptrap_oid[];

extern const size_t snmptrap_oid_len;

// Imported in "snmpstats.c". The values are defined in kamailio.cfg
extern char *target_ip;
extern char *target_port;
extern char *target_community;

/*
 * This function is not working yet. Probably because the snmp conf file is not
 * correctly setup.
 * Uses alternatively send_current_dialog_notification() function which does not use
 * the sub-agent from snmpstats module but initializes a new snmp session
 * to send out traps directly to the target machine.
 */
int send_DialogNotification_trap(CurrentDialogTable_context_str *context) {

    netsnmp_variable_list *var_list = NULL;

    /* Notification's OID */
    const oid       DialogNotification_oid[] = { 1, 3, 6, 1, 4, 1, 3001, 1, 1 };

    /* Variable-binding's OID */
    oid       CallId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 3, 2, 1, 1, 0  };
    oid       CallerId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 3, 2, 1, 2, 0  };
    oid       CallerProxyId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 3, 2, 1, 3, 0  };
    oid       CalleeId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 3, 2, 1, 4, 0  };
    oid       CalleeProxyId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 3, 2, 1, 5, 0  };
    oid       CallType_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 3, 2, 1, 6, 0  };
    oid       CallState_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 3, 2, 1, 7, 0  };

    /*
     * Set the snmpTrapOid.0 value
     */
    snmp_varlist_add_variable(&var_list,
                              snmptrap_oid, snmptrap_oid_len,
                              ASN_OBJECT_ID,
                              DialogNotification_oid, sizeof(DialogNotification_oid));

    /*
     * Add any objects from the trap definition. (Variable-Bindings)
     */
    // CallId
    snmp_varlist_add_variable(&var_list,
                              CallId_oid, OID_LENGTH(CallId_oid),
                              ASN_OCTET_STR,
                              "send_DialogNotification_trap", sizeof("send_DialogNotification_trap"));
    // CallerId
    snmp_varlist_add_variable(&var_list,
                              CallerId_oid, OID_LENGTH(CallerId_oid),
                              ASN_UNSIGNED,
                              (u_char *) &context->CallerId, sizeof(context->CallerId));
    // CallerProxyId
    snmp_varlist_add_variable(&var_list,
                              CallerProxyId_oid,
                              OID_LENGTH(CallerProxyId_oid), ASN_UNSIGNED,
                              (u_char *) &context->CallerProxyId, sizeof(context->CallerProxyId));
    // CalleeId
    snmp_varlist_add_variable(&var_list,
                              CalleeId_oid, OID_LENGTH(CalleeId_oid),
                              ASN_UNSIGNED,
                              (u_char *) &context->CalleeId, sizeof(context->CalleeId));
    // CalleeProxyId
    snmp_varlist_add_variable(&var_list,
                              CalleeProxyId_oid,
                              OID_LENGTH(CalleeProxyId_oid), ASN_UNSIGNED,
                              (u_char *) &context->CalleeProxyId, sizeof(context->CalleeProxyId));
    // CallType
    snmp_varlist_add_variable(&var_list,
                              CallType_oid, OID_LENGTH(CallType_oid),
                              ASN_INTEGER,
                              (u_char *) &context->CallType, sizeof(context->CallType));
    // CallState
    snmp_varlist_add_variable(&var_list,
                              CallState_oid, OID_LENGTH(CallState_oid),
                              ASN_INTEGER,
                              (u_char *) &context->CallState, sizeof(context->CallState));

    /*
     * Send the trap to the list of configured destinations
     *  and clean up
     */
    send_v2trap(var_list);
    snmp_free_varbind(var_list);

    return SNMP_ERR_NOERROR;
}

/*
 * This function initializes a new snmp session to send out traps directly to
 * the target machine.
 */
int send_current_dialog_notification(CurrentDialogTable_context_str *context) {

    /* Notification's OID */
    const char *DialogNotification_oid = "1.3.6.1.4.1.3001.1.1";
    // const oid       DialogNotification_oid[] = { 1, 3, 6, 1, 4, 1, 3001, 1, 1 };

    /* Variable-binding's OID */
    oid       CallId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 1, 1, 1  };

    oid       CallerId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 1, 1, 2  };

    oid       CallerProxyId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 1, 1, 3  };

    oid       CalleeId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 1, 1, 4  };

    oid       CalleeProxyId_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 1, 1, 5  };

    oid       CallType_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 1, 1, 6  };

    oid       CallState_oid[] =
    { 1, 3, 6, 1, 4, 1, 3001, 1, 1, 7  };

    char *config = "quantum";

    init_snmp(config);
    struct snmp_session session, *snmpWorker;
    snmp_sess_init(&session);
    session.version = SNMP_VERSION_2c;

    char target[22];
    sprintf(target, "%s:%s", target_ip, target_port);
    session.peername = target;
    session.community = (unsigned char *)target_community;
    session.community_len = strlen((char *)session.community);
    snmpWorker = snmp_open(&session);
    if (!snmpWorker) {
        LM_ERR("snmp_open session failed.");
        return -1;
    }
    LM_INFO("Sending SNMP notification to %s.\n", session.peername);

    oid oid_sysuptime[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };

    oid oid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };

    netsnmp_pdu *pdu = NULL;
    pdu = snmp_pdu_create(SNMP_MSG_TRAP2);

    // Prepare system up-time
    long sysuptime;
    char tempbuf[128];
    sysuptime = get_uptime();
    sprintf(tempbuf, "%ld", sysuptime);
    memset(tempbuf, 128, 0);

    // Prepare notification context
    snmp_add_var(pdu, oid_sysuptime, OID_LENGTH(oid_sysuptime), 't', tempbuf);

    snmp_add_var(pdu, oid_snmptrap,  OID_LENGTH(oid_snmptrap), 'o', DialogNotification_oid);

    /*
    LM_INFO("=== NOTIFICATION DATA ===");
    LM_INFO("CallId=%s, CallType=%s, CallState=%s", context->CallId, context->CallType, context->CallState);
    LM_INFO("Caller=%s@%s, Callee=%s@%s",context->CallerId,context->CallerProxyId,context->CalleeId,context->CalleeProxyId);
    LM_INFO("*************************");
    */

    snmp_add_var(pdu, CallId_oid,        OID_LENGTH(CallId_oid),        's', context->CallId);
    snmp_add_var(pdu, CallerId_oid,      OID_LENGTH(CallerId_oid),      'u', context->CallerId);
    snmp_add_var(pdu, CallerProxyId_oid, OID_LENGTH(CallerProxyId_oid), 'u', context->CallerProxyId);
    snmp_add_var(pdu, CalleeId_oid,      OID_LENGTH(CalleeId_oid),      'u', context->CalleeId);
    snmp_add_var(pdu, CalleeProxyId_oid, OID_LENGTH(CalleeProxyId_oid), 'u', context->CalleeProxyId);
    snmp_add_var(pdu, CallType_oid,      OID_LENGTH(CallType_oid),      'u', context->CallType);
    snmp_add_var(pdu, CallState_oid,     OID_LENGTH(CallState_oid),     'u', context->CallState);

    if (!snmp_send(snmpWorker, pdu)) {
        LM_ERR("Failed to send the notification.\n");
    }

    snmp_close(snmpWorker);
    snmp_shutdown(config);

    return 0;
}
